package com.donglan.pethome.user.service.impl;

import com.donglan.pethome.basic.constant.VerificationCode;
import com.donglan.pethome.basic.service.impl.BaseServiceImpl;
import com.donglan.pethome.basic.util.*;
import com.donglan.pethome.user.constant.UserConstant;
import com.donglan.pethome.user.domain.User;
import com.donglan.pethome.user.domain.vo.UserVo;
import com.donglan.pethome.user.mapper.UserMapper;
import com.donglan.pethome.user.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: zhangdonghui
 * @Date: 2022/1/14 16:47
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    /*
     * 功能描述 校验手机号码是否存在
     * @author zhangdonghui
     * @date 2022/1/14 16:49
     * @param phone 手机号码
     * @return void
     */
    @Override
    public void checkPhone(String phone) {
        //通过手机号码查询用户对象
        User user = userMapper.loadByPhone(phone);
        //如果手机号码不为空，则抛出异常
        if (user != null) {
            throw new RuntimeException("手机号码已被注册!!");
        }
    }

    /*
     * 功能描述 发送手机验证码
     * @author zhangdonghui
     * @date 2022/1/14 17:39
     * @param phone
     * @return void
     */
    @Override
    public void sendMobileCodeByPhone(String phone) {
        //产生随机4位字符作为验证码而存在
        String code = StrUtil.getComplexRandomString(4);

        // 通过key获取redis中对应的值   格式： "USER_REG:phone"，"验证码:时间戳"
        String value = RedisUtil.INSTANCE.get(VerificationCode.USER_REG + phone);

        // 如果value不为空，就代表验证码没有过期
        if (StringUtils.isNotBlank(value)) {
            // 以:截取字符串，返回两个元素的数组，第一个元素sphone，第二个元素time
            // 并且最终转为Long类型 获得上一次验证码发送的时间戳      值: 时间戳
            Long beginTime = Long.valueOf(value.split(":")[1]);
            //如果第二次时间减去上一次的时间是小1分钟，就证明一分钟以内发送了多次请求
            if (System.currentTimeMillis() - beginTime < 60 * 1000) {
                throw new RuntimeException("1分钟以内请别发送多次请求!!");
            }
            //如果验证码超过了一分钟，我又点击了一次发送请求，这时候我使用的验证码应该是之前的验证码
            code = value.split(":")[0];
        }

        //设置有效期是10分钟 格式： "USER_REG:phone" ，"验证码:时间戳"
        RedisUtil.INSTANCE.set(VerificationCode.USER_REG + phone, code + ":" + System.currentTimeMillis(), 10 * 60);

        //用打印语句表示发送短信
        String text = "尊敬的客户,您的验证码是:" + code + ",请在10分钟以内完成注册操作!!";

        //开始发送短信
        //SendMsgUtil.sendCode(phone, text);
        System.out.println(text);
    }

    /*
     * 功能描述 手机号码注册用户
     * @author zhangdonghui
     * @date 2022/1/18 16:29
     * @param userVo
     * @return void
     */
    @Override
    public void register(UserVo userVo) {
        // 1、校验前端传递的数据
        verifyPhone(userVo);
        // 2、把UserVo转为User对象
        User user = userVoToUser(userVo);
        // 3、保存User对象
        super.add(user);
    }



    /*
     * 功能描述 临时的user对象转User对象
     * @author zhangdonghui
     * @date 2022/1/18 16:58
     * @param userVo
     * @return com.donglan.pethome.user.domain.User
     */
    private User userVoToUser(UserVo userVo) {
        User user = new User();
        user.setPhone(userVo.getPhone());
        user.setSalt(StrUtil.getComplexRandomString(10));
        user.setPassword(MD5Utils.encrypByMd5(userVo.getPassword()+user.getSalt()));
        /*
         * 直接在User实体类中给createTime初始化了即可，
         * 当然也可以这样写，二者选一
         * */
        //user.setCreatetime(new Date());
        /*
         * 设置用户的状态为激活的状态
         * 这里我们也优化一下，将状态的设置为常量
         * 但是因为是用户模块的，所以我们在User包下面新建常量包常量类
         * 这样做是为了后期代码的维护
         */
        //user.setState(1);
        user.setState(UserConstant.ACTIVATION);
        return user;
    }

    /*
     * 功能描述 校验前端传递的数据
     * @author zhangdonghui
     * @date 2022/1/18 16:33
     * @param userVo
     * @return com.donglan.pethome.basic.util.AjaxResult
     */
    private void verifyPhone(UserVo userVo) {
        //手机认证 null和是否存在
        if (StringUtils.isBlank(userVo.getPhone())|| StringUtils.isBlank(userVo.getCode())
                || StringUtils.isBlank(userVo.getPassword()) || StringUtils.isBlank(userVo.getConfirmPassword()))
            throw  new RuntimeException("请输入相关参数");

        //密码和确认密码校验
        if (!userVo.getPassword().equals(userVo.getConfirmPassword()))
            throw new RuntimeException("两次密码输入不一致");

        /*
         * 短信验证码校验
         * 为了避免每次使用USER_REG字符串可能引起的单词错误，我们将其封装成常量来调用
         * 在basic包中创建constant包，创建VerificationCode类，设置key常量
         * 凡是使用 "USER_REG"字符串形式的都更新一下
         * */
        // 获取验证码，通过对应的key获取对应的值，格式为：code:毫秒值
        String codeString = RedisUtil.INSTANCE.get(VerificationCode.USER_REG + userVo.getPhone());
        // 如果codeString为空，就代表验证码已经过期
        if(StringUtils.isBlank(codeString))
            throw  new RuntimeException("验证码已过期");
        // 如果不为空，那就获取验证码，将毫秒值分隔
        String code = codeString.split(":")[0];
        // 将从redis中获取的验证码与请求过来的参数验证码进行比较
        if (!code.equalsIgnoreCase(userVo.getCode()))
            throw  new RuntimeException("验证码错误");
    }


    /*
     * 功能描述 通过用户名和密码查询用户对象
     * @author zhangdonghui
     * @date 2022/1/20 13:37
     * @param username
     * @param password
     * @return com.donglan.pethome.user.domain.User
     */
    @Override
    public User loadByUsernameAndPassword(String username, String password) {
        Map<String,Object> map = new HashMap<>();
        map.put("username",username);
        map.put("state","1");
        // 通过用户名查询用户对象，根据页面的用户名包括：用户名、邮箱和电话
        User user = userMapper.loadByUsernameOrEmailOrPhone(map);
        if(user == null)
            throw new RuntimeException("用户不存在");

        // 校验加密后的密码是否一致，加密方式为：拼接盐值进行md5加密
        if(!user.getPassword().equals(MD5Utils.encrypByMd5(password+user.getSalt())))
            throw new RuntimeException("密码错误");
        return user;
    }

}
